import React from "react";
import MenuOptionsPanel from "@components/MenuOptionsPanel";
import Ys4funToolsAppContext from "@/contexts/Ys4funToolsContext";
import { MENU_OPTION_TAG_CONFIG_DATA_LIST } from "@/configs";
import { IYs4funToolsAppContextProps, IYs4funToolsAppState, IMenuOptionTagConfigDataMap } from "@/types"
import "./Ys4funToolsApp.css";

class Ys4funToolsApp extends React.Component<{}, IYs4funToolsAppState> {
  private dragRef = React.createRef<HTMLDivElement>();
  private menuOptionTagConfigDataList: IMenuOptionTagConfigDataMap[];

  state: IYs4funToolsAppState = {
    toolsDragConfig: {
      isDraggable: false,
      isDragging: false,
      isDragReady: false,
      position: { x: 0, y: 0 },
      offset: { x: 0, y: 0 },
      eleSize: { w: 150, h: 150 },
      pressTimer: null,
    },
  };

  constructor(prop: IYs4funToolsAppContextProps) {
    super(prop);
    this.menuOptionTagConfigDataList = MENU_OPTION_TAG_CONFIG_DATA_LIST;
  }

  componentDidMount() {
    if (this.dragRef.current) {
      const rect = this.dragRef.current.getBoundingClientRect();
      this.setState(prevState => ({
        toolsDragConfig: {
          ...prevState.toolsDragConfig,
          eleSize: { w: rect.width, h: rect.height, }
        }
      }))
    }
  }

  componentWillUnmount() {
    this.removeEventListeners();
    this.clearPressTimer();
  }

  clearPressTimer = (): void => {
    const { toolsDragConfig } = this.state;
    if (toolsDragConfig.pressTimer) {
      clearTimeout(toolsDragConfig.pressTimer);
      this.setState(prevState => ({
        toolsDragConfig: {
          ...prevState.toolsDragConfig,
          pressTimer: null
        }
      }));
    }
  }

  addEventListeners = () => {
    window.addEventListener('mousemove', this.handleMouseMove);
    window.addEventListener('mouseup', this.handleMouseUp);
  }

  removeEventListeners = () => {
    window.removeEventListener('mousemove', this.handleMouseMove);
    window.removeEventListener('mouseup', this.handleMouseUp);
  }

  toggleDraggable = () => {
    this.setState(prevState => ({
      ...prevState,
      toolsDragConfig: {
        ...prevState.toolsDragConfig,
        isDraggable: !prevState.toolsDragConfig.isDraggable,
        isDragReady: false,
      }
    }));
  }

  setDragReady = (ready: boolean) => {
    this.setState(prevState => ({
      toolsDragConfig: {
        ...prevState.toolsDragConfig,
        isDragReady: ready
      }
    }));
  }

  updatePosition = (x: number, y: number) => {
    this.setState(prevState => ({
      toolsDragConfig: {
        ...prevState.toolsDragConfig,
        position: { x, y }
      }
    }));
  }

  handleMouseDown = (event: React.MouseEvent) => {
    const isFromMenuPanel = (event.target as HTMLElement).closest(".menu-options-panel");
    if (!isFromMenuPanel || !this.state.toolsDragConfig.isDraggable) return;

    this.addEventListeners();

    const timer = setTimeout(() => {
      this.setDragReady(true);

      this.setState(prevState => ({
        toolsDragConfig: {
          ...prevState.toolsDragConfig,
          isDragging: true,
        }
      }));

      if (this.dragRef.current) {
        const rect = this.dragRef.current.getBoundingClientRect();
        this.setState(prevState => ({
          toolsDragConfig: {
            ...prevState.toolsDragConfig,
            offset: {
              x: event.clientX - rect.left,
              y: event.clientY - rect.top,
            }
          }
        }));
      }
    }, 100);

    this.setState(prevState => ({
      toolsDragConfig: {
        ...prevState.toolsDragConfig,
        pressTimer: timer,
      }
    }));
  }

  handleMouseMove = (event: MouseEvent) => {
    const { isDragReady, isDragging, isDraggable, offset, eleSize } = this.state.toolsDragConfig;

    if (!isDragReady || !isDragging || isDraggable) return;

    let x = event.clientX - offset.x;
    let y = event.clientY - offset.x;

    const windowWidth = window.innerWidth;
    const windowHeight = window.innerHeight;

    x = Math.max(1, Math.min(x, windowWidth - eleSize.w));
    y = Math.max(1, Math.min(y, windowHeight - eleSize.h));

    this.updatePosition(x, y);
  }

  handleMouseUp = () => {
    this.clearPressTimer();
    this.removeEventListeners();
    this.setState(prevState => ({
      toolsDragConfig: {
        ...prevState.toolsDragConfig,
        isDragging: false,
        isDragReady: false,
      }
    }));
  }

  render(): React.ReactNode {
    const { toolsDragConfig: { position, isDragReady, isDragging } } = this.state;
    return (
      <Ys4funToolsAppContext.Provider value={{
        data: { ...this.state, },
        config: { menuOptionTagConfigDataList: this.menuOptionTagConfigDataList },
        methods: { updatePosition: this.updatePosition, toggleDraggable: this.toggleDraggable, setDragReady: this.setDragReady }
      }}>
        <div className="Ys4fun-Tools-App" 
          ref={this.dragRef} 
          onMouseDown={this.handleMouseDown}
          style={{
            left: position.x > 0 ? position.x + "px" : "inherit",
            top: position.y > 0 ? position.y + "px" : "inherit",
            cursor: isDragReady ? (isDragging ? "move" : "pointer") : "pointer",
          }}
        >
          <MenuOptionsPanel />
        </div>
      </Ys4funToolsAppContext.Provider>
    )
  }
}

export default Ys4funToolsApp;
